package com.weipai.demo.util;

import com.alipay.api.AlipayApiException;
import com.weipai.demo.enums.RetCode;
import com.weipai.demo.model.PayOrder;
import github.wxpay.sdk.WXPayConstants;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static github.wxpay.sdk.WXPayUtil.*;

public class WxPayUtil {
    private final static String APP_ID = PropUtils.getString("appId");

    private final static String MCH_ID = PropUtils.getString("mchId");

    private final static String APP_KEY = PropUtils.getString("key");

    public final static String Notify_Url = PropUtils.getString("Notify_Url");

    private final static String wxEwmPayNotify_Url = PropUtils.getString("wxEwmPayNotify_Url");
    public final static String basePath = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单请求地址

    private final static String NOTIFY_URL = PropUtils.getString("NOTIFY_URL");
    private final static String ALIPAY_FORMAT = "json";
    private final static String ALIPAY_CHARSET = "UTF-8";
    private final static String SIGN_TYPE = "RSA2";

    /**
     * 单笔转账到支付宝账户
     * https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.54Ty29&treeId=193&articleId=106236&docType=1
     *
     * @return
     * @throws AlipayApiException
     */
    public static Response createQRCode(HttpServletRequest request, HttpServletResponse res, PayOrder payOrder)  {
        Response response = new Response();
        ServletOutputStream sos = null;
        try {
            String orderInfo = createOrderInfo(payOrder,request);//生成【统一下单API】所需参数的接口
            Map<String ,String> map= httpOrder(orderInfo);//调用统一下单接口
            //返回字段很多，这里只取我们所需的字段
            String return_msg = map.get("return_msg");//返回信息
            String return_code = map.get("return_code");//状态码
            String result_code = map.get("result_code");//业务结果
            String code_url = map.get("code_url");
            //根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
            if (null != map && "SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
                if(code_url == null){
                    response.setCode(RetCode.PAY_FAILD);
                    return response;
                }
                QRCodeUtil.encode(code_url, "F:/javapro/loans/temp-rainy/");//调用生成二维码的方法
//                sos = res.getOutputStream();
//            QRCodeUtil.encode(code_url, sos);//调用生成二维码的方法
                response.setCode(RetCode.SUCCESS);
            } else {
                response.setCode(RetCode.PAY_FAILD);
//                response.setData(return_msg);
                response.setMsg(return_msg);
                return response;
            }

        } catch (IOException e) {
            response.setCode(RetCode.PAY_FAILD);
            response.setData(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            response.setCode(RetCode.PAY_FAILD);
            response.setData(e.getMessage());
            e.printStackTrace();
        }
        return response;
    }

    /**
     * 调统一下单API
     * @param orderInfo
     * @return
     */
    private static Map<String,String> httpOrder(String orderInfo) {
        String url = basePath;
        Map<String, String> map = new HashMap<>();
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
            //加入数据
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);

            BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
            buffOutStr.write(orderInfo.getBytes("UTF-8"));
            buffOutStr.flush();
            buffOutStr.close();

            //获取输入流
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

            String line = null;
            StringBuffer sb = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            map = xmlToMap(sb.toString());
            return map;
        } catch (Exception e) {
            map.put("return_msg",e.getMessage());
            map.put("result_code","ERROR");
            map.put("return_code","ERROR");
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 生成统一下单格式的订单，生成一个XML格式的字符串
     * @param orderId
     * @return
     */
    private static String createOrderInfo(PayOrder payOrder, HttpServletRequest request) throws Exception {
//        Products products = productsService.selectByPrimaryKey(Long.valueOf(productid));//商品对象
//        ProductOrders productOrders = this.proOrdersService.selectByOrderId(orderId);//订单信息
        //生成订单对象
        Map<String, String> map = new HashMap<>();
        map.put("appid", APP_ID);//公众账号ID
        map.put("mch_id", MCH_ID);//商户号
        map.put("body", "信贷经理入驻");//商品描述
        map.put("nonce_str", UUIDUtils.getUUID());
        map.put("notify_url", Notify_Url);//通知地址
        map.put("out_trade_no", payOrder.getOrderSn());//订单号
        map.put("spbill_create_ip", WxPayMessage.getIpAddress(request));//终端ip
        map.put("trade_type", "NATIVE");//交易类型
        map.put("total_fee", String.valueOf(payOrder.getPayMoney().multiply(new BigDecimal(100))));//总金额
        String sign = createSign(map, APP_KEY);//调用生成签名的方法，用以Map集合中的相关参数生成签名
        map.put("sign", sign);//签名
        //将订单对象转为xml格式
        String s = null;
        try {
            return mapToXml(map);//maptoXml方法是微信sdk自带的方法
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new String(s.getBytes("UTF-8"));
    }

    /**
     * 生成签名
     * 这个方法是从微信sdk里copy过来的，自己也可以写，要注意生成签名后UTF-8的转换，要不然容易报签名Body UTF-8错误
     * @param data 待签名数据
     * @param key  API密钥
     */
    public static String createSign(final Map<String, String> data, String key) throws Exception {
        return createSign(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data     待签名数据
     * @param key      API密钥
     * @param signType 签名方式
     * @return 签名
     */
    private static String createSign(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        //根据规则创建可排序的map集合
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空，则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        //转换UTF-8
        String str = new String(sb.toString().getBytes("UTF-8"));
        if (WXPayConstants.SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        } else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }
}
